﻿# define _CRT_SECURE_NO_WARNINGS 1
//23讲 文件操作
//1. 为什么使⽤⽂件？
//如果没有⽂件，我们写的程序的数据是存储在电脑的内存中，如果程序退出，内存回收，数据就丢失
//了，等再次运⾏程序，是看不到上次程序的数据的，如果要将数据进⾏持久化的保存，我们可以使⽤
//⽂件。
// #include <stdio.h>
//int main()
//{
//	int a = 0;
//	scanf("%d", &a);
//	printf("%d\n", a);//我们写的程序的数据是存储在电脑的内存中，如果程序退出，内存回收，数据就丢失
//	return 0;
//}
//2. 什么是⽂件？
//磁盘（硬盘）上的⽂件是⽂件。
//但是在程序设计中，我们⼀般谈的⽂件有两种：程序⽂件、数据⽂件（从⽂件功能的⻆度来分类
//的）。
//2.1 程序⽂件
//程序⽂件包括源程序⽂件（后缀为.c）, ⽬标⽂件（windows环境后缀为.obj）, 可执⾏程序（windows
//环境后缀为.exe）。
//2.2 数据⽂件
//⽂件的内容不⼀定是程序，⽽是程序运⾏时读写的数据，⽐如程序运⾏需要从中读取数据的⽂件，或
//者输出内容的⽂件。本章讨论的是数据⽂件。
//在以前各章所处理数据的输⼊输出都是以终端为对象的，即从终端的键盘输⼊数据，运⾏结果显⽰到
//显⽰器上。
//在以前各章所处理数据的输⼊输出都是以终端为对象的，即从终端的键盘输⼊数据，运⾏结果显⽰到
//显⽰器上。
//其实有时候我们会把信息输出到磁盘上，当需要的时候再从磁盘上把数据读取到内存中使⽤，这⾥处
//理的就是磁盘上⽂件。
//2.3 ⽂件名
//⼀个⽂件要有⼀个唯⼀的⽂件标识，以便⽤⼾识别和引⽤。
//⽂件名包含3部分：⽂件路径 + ⽂件名主⼲ + ⽂件后缀
//例如： c : \code\test.txt
//为了⽅便起⻅，⽂件标识常被称为⽂件名。
//2.3 ⽂件名
//⼀个⽂件要有⼀个唯⼀的⽂件标识，以便⽤⼾识别和引⽤。
//⽂件名包含3部分：⽂件路径 + ⽂件名主⼲ + ⽂件后缀
//例如： c : \code\test.txt
//为了⽅便起⻅，⽂件标识常被称为⽂件名。
//3. ⼆进制⽂件和⽂本⽂件
//根据数据的组织形式，数据⽂件被分为⽂本⽂件和⼆进制⽂件。
//数据在内存中以⼆进制的形式存储，如果不加转换的输出到外存的⽂件中，就是⼆进制⽂件。
//如果要求在外存上以ASCII码的形式存储，则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂
//本⽂件。
//⼀个数据在⽂件中是怎么存储的呢？
//字符⼀律以ASCII形式存储，数值型数据既可以⽤ASCII形式存储，也可以使⽤⼆进制形式存储。
//如有整数10000，如果以ASCII码的形式输出到磁盘，则磁盘中占⽤5个字节（每个字符⼀个字节），⽽
//⼆进制形式输出，则在磁盘上只占4个字节。
//#include <stdio.h>
//int main()
//{
//	int a = 10000;
//	FILE* pf = fopen("test.txt", "wb");
//	fwrite(&a, 4, 1, pf);//⼆进制的形式写到⽂件中  
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//文件操作
// 打开文件
// 读/写文件
// 关闭文件
//文件 ——-：容器（存储空间：可以想象成瓶子）

//4.1 流和标准流
//4.1.1 流
//我们程序的数据需要输出到各种外部设备，也需要从外部设备获取数据，不同的外部设备的输⼊输出
//操作各不相同，为了⽅便程序员对各种设备进⾏⽅便的操作，我们抽象出了流的概念，我们可以把流
//想象成流淌着字符的河。C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
//⼀般情况下，我们要想向流⾥写数据，或者从流中读取数据，都是要打开流，然后操作。
//4.1.2 标准流
//那为什么我们从盘输⼊数据，向屏幕上输出数据，并没有打开流呢？
//那是因为C语⾔程序在启动的时候，默认打开了3个流：
//• stdin-标准输⼊流，在⼤多数的环境中从键盘输⼊，scanf函数就是从标准输⼊流中读取数据。
//• stdout-标准输出流，⼤多数的环境中输出⾄显⽰器界⾯，printf函数就是将信息输出到标准输出
//流中。
//• stderr - 标准错误流，⼤多数环境中输出到显⽰器界⾯。
//这是默认打开了这三个流，我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
//stdin、stdout、stderr三个流的类型是： FILE * ，通常称为⽂件指针。
//C语⾔中，就是通过 FILE * 的⽂件指针来维护流的各种操作的。
//4.2 ⽂件指针
// 4.3 ⽂件的打开和关闭
//⽂件在读写之前应该先打开⽂件，在使⽤结束之后应该关闭⽂件。
//在编写程序的时候，在打开⽂件的同时，都会返回⼀个FILE* 的指针变量指向该⽂件，也相当于建⽴了
//指针和⽂件的关系。
//ANSIC规定使⽤ fopen 函数来打开⽂件， fclose 来关闭⽂件。
//4.3.1  fopen
//代码块
////打开⽂件 
//FILE * fopen(const char* filename, const char* mode);
//2功能： fopen 函数是⽤来打开参数 filename 指定的⽂件，同时将打开的⽂件和⼀个流进⾏关联，
//后续对流的操作是通过 fopen 函数返回的指针来维护。具体对流（关联的⽂件）的操作是通过参数
//mode 来指定的。
//参数：
//filename ：表⽰被打开的⽂件的名字，这个名字可以绝对路径，也可以是相对路径。
//mode ：表⽰对打开的⽂件的操作⽅式，具体⻅下⾯的表格。
//返回值：
//• 若⽂件成功打开，该函数将返回⼀个指向FILE 对象的指针，该指针可⽤于后续操作中标识对应
//的流。
//• 若打开失败，则返回 NULL 指针，所以⼀定要 fopen 的返回值做判断，来验证⽂件是否打开成
//功。
/*mode表⽰对打开的⽂件的操作⽅式：
⽂件使⽤⽅式       含义                             如果指定⽂件不存在
“r”（只读）   为了输⼊数据，打开⼀个已经存在的⽂本⽂件    出错
“w”（只写）   为了输出数据，打开⼀个⽂本⽂件            建⽴⼀个新的⽂件
“a”（追加）  向⽂本⽂件尾添加数据                      建⽴⼀个新的⽂件
rb”（只读）  为了输⼊数据，打开⼀个⼆进制⽂件            出错
“wb”（只写）  为了输出数据，打开⼀个⼆进制⽂件          建⽴⼀个新的⽂件
“ab”（追加） 向⼀个⼆进制⽂件尾添加数据                建⽴⼀个新的⽂件
“r + ”（读写） 为了读和写，打开⼀个⽂本⽂件           出错
“w + ”（读写） 为了读和写，建议⼀个新的⽂件             建⽴⼀个新的⽂件
“a + ”（读写） 打开⼀个⽂件，在⽂件尾进⾏读写           建⽴⼀个新的⽂件
“rb + ”（读写）  为了读和写打开⼀个⼆进制⽂件           出错
“wb + ”（读写） 为了读和写，新建⼀个新的⼆进制⽂         建⽴⼀个新的⽂件
“ab + ”（读写） 打开⼀个⼆进制⽂件，在⽂件尾进⾏读和写    建⽴⼀个新的⽂
4.3.2 fclose
fclose函数原型
int fclose(FILE * stream);
功能：关闭参数 stream 关联的⽂件，并取消其关联关系。与该流关联的所有内部缓冲区均会解除关
联并刷新：任何未写⼊的输出缓冲区内容将被写⼊，任何未读取的输⼊缓冲区内容将被丢弃。
参数：
stream ：指向要关闭的流的  FILE 对象的指针
返回值：成功关闭 stream 指向的流会返回0，否则会返回 EOF*/ 
//代码演⽰：
//#include <stdio.h>
//int main()
//{
//	//.  表示当前路径
//	//.. 表示以当前路径的上一路径
//	//FILE* pf = fopen("data.txt","w");
//	FILE* pf = fopen("./../../text//text2//data.txt","w");
//	//z这里的data.txt是相对路径,表示当前工程目录下的data.tx
//	//绝对路径        \\:双斜线防止被认为为转义字符
//	//FILE* pf = fopen("C:\\Users\\王思宇\\Desktop\\data.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen/n");
//		return 1;
//	}
//	//读，写文件
//	//
//	//关闭文件
//	flcose(pf);
//	pf = NULL;//将指针置为NULL,避免成为野指针。
//	return 0;
//}
//5. ⽂件的顺序读写
//在进⾏⽂件读写的时候，会涉及下⾯这些函数，我们⼀ 给⼤家介绍：
//函数名    功能                          适⽤于
//fgetc     从输⼊流中读取⼀个字符          所有输⼊流
//fputc     向输出流中写⼊⼀个字符          所有输出流
//fgets    从输⼊流中读取⼀个字符串        所有输⼊流
//fputs    向输出流中写⼊⼀个字符串        所有输出流
//fscanf从 输⼊流中读取带有格式的数据      所有输⼊流
//fprintf 向输出流中写⼊带有格式的数据     所有输出流
//fread   从输⼊流中读取⼀块数据          ⽂件输⼊流
//fwrite  向输出流中写⼊⼀块数据          ⽂件输出流
//5.1 fputc
//fputc函数原型
//int fputc(int character, FILE * stream);
//功能：将参数 character 指定的字符写⼊到 stream 指向的输出流中，通常⽤于向⽂件或标准输
//出流写⼊字符。在写⼊字符之后，还会调整指⽰器。字符会被写⼊流内部位置指⽰器当前指向的位
//置，随后该指⽰器⾃动向前移动⼀个位置。
//1参数：
//character ：被写⼊的字符
//stream ：是⼀个FILE * 类型的指针，指向了输出流（通常是⽂件流或stdout）。
//返回值：
//• 成功时返回写⼊的字符（以int 形式）。
//• 失败时返回EOF （通常是 - 1），错误指⽰器会被设置，可通过ferror() 检查具体错误。
//#include <stdio.h>
//int main()
//{
//	//打开文件
//	FILE * pf =fopen("wang.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//写文件
//	//fputc('a', pf);
//	//int i = 0;
//	//for (i = 'a'; i <= 'z'; i++)
//	//{
//	//	fputc(i, pf);
//	//}
//	// 也可以在标准输出流stdout中写入
//	int i = 0;
//	for (i = 'a'; i <= 'z'; i++)
//		{
//			fputc(i, stdout);
//		}	////关闭文件
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//5.2 fgetc
//fgetc函数的原型
//int fgetc(FILE * stream);
//功能：从参数 stream 指向的流中读取⼀个字符。函数返回的是⽂件指⽰器当前指向的字符，读取这
//个字符之后，⽂件指⽰器⾃动前进道下⼀个字符。
//参数：
//stream : FILE* 类型的⽂件指针，可以是 stdin ，也可以是其他输⼊流的指针。如果是 stdin 就
//从标准输⼊流读取数据。如果是⽂件流指针，就从⽂件读取数据。
//返回值：
//• 成功时返回读取的字符（以int 形式）。
//• 若调⽤时流已处于⽂件末尾，函数返回EOF 并设置流的⽂件结束指⽰器（ feof ）。
//若发⽣读取错误，函数返回EOF 并设置流的错误指⽰器（ ferror ）。
//#include <stdio.h>
//int main()
//{
//	//打开文件
//	FILE* pf = fopen("wang.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//读文件
//	//int a = fgetc(pf);
//	int i = 0;
//	for (i = 0; i <= 10; i++)
//	{
//		int a = fgetc(pf);
//		printf("%c ", a);
//	}
//	//关闭文件
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//5.3 feof和ferror
//代码块
//int feof(FILE * stream);//eof =end of file
////检测stream指针指向的流是否遇到⽂件末尾 
//int ferror(FILE* stream);
////检测stream指针指向的流是否发⽣读/写错误 
//如果在读取⽂件的过程中，遇到了⽂件末尾，⽂件读取就会结束。这时读取函数会在对应的流上设置
//⼀个⽂件结束的指⽰符，这个⽂件结束指⽰符可以通过 feof 函数检测到。如果 feof 函数检测到⽂
//件结束指⽰符已经被设置，则返回⾮0的值，如果没有设置则返回0。
//如果在读 / 写⽂件的过程中，发⽣了读 / 写错误，⽂件读取就会结束。这时读 / 写函数会在对应的流上设
//置⼀个错误指⽰符，这个错误指⽰符可以通过 ferror 函数检测到。如果 ferror 函数检测到错误
//指⽰符已经被设置，则返回⾮0的值，如果没有设置则返回0。
//feof
 /*        遇到了⽂件末尾  设置wen件结束指⽰符    则返回⾮0的值
读取⽂件
         没遇到了⽂件末尾                      没有设置则返回0*/
//5.4 fputs
//fputs函数原型
//int fputs(const char* str, FILE * stream);
//功能：将参数 str 指向的字符串写⼊到参数 stream 指定的流中（不包含结尾的空字符\0 ），适
//⽤于⽂件流或标准输出（stdout）。
//参数：
//str : str是指针，指向了要写⼊的字符串（必须\0 结尾）
//stream ：是⼀个 FILE * 的指针，指向了要写⼊字符串的流。
//返回值：
//• 成功时返回⾮负整数。
//• 失败时返回EOF （ - 1），同时会设置流的错误指⽰器，可以使⽤ ferror() 检查错误原因。
//#include <stdio.h>
//int main()
//{
//    FILE*pf = fopen("wang.txt", "w");
//    if (pf == NULL)
//    {
//        perror("fopen");
//        return 1;
//    }
//    //写文件
//    /*fputs("abc", pf);
//    fputs("def", pf);*/
//    //实际写入效果：abcdef  连着写
//
//    fputs("abc\n", pf);
//    fputs("def", pf);
//    // 可自己加\n
//      //关闭文件
//    fclose(pf);
//    pf = NULL;
//    return 0;
//}
//5.5 fgets
//fgets函数原型
//char* fgets(char* str, int num, FILE * stream);
//功能：从 stream 指定输⼊流中读取字符串，⾄读取到换⾏符、⽂件末尾（EOF）或达到指定字符数
//（包含结尾的空字符\0 ），然后将读取到的字符串存储到str指向的空间中。
//参数：
//• str ：是指向字符数组的指针，str指向的空间⽤于存储读取到的字符串。
//• num ：最⼤读取字符数（包含结尾的\0 ，实际最多读取num - 1 个字符）。
//• stream ：输⼊流的⽂件指针（如⽂件流或stdin ）。
//返回值：
//• 成功时返回str 指针。
//• 若在尝试读取字符时遇到⽂件末尾，则设置⽂件结束指⽰器，并返回NULL ，需通过feof()
//检测。
//• 若发⽣读取错误，则设置流错误指⽰器，并返回NULL，通过 ferror() 检测。
//#include <stdio.h>
//int main()
//{   //打开文件
//    FILE* pf = fopen("wang.txt", "r");
//    if (pf == NULL)
//    {
//        perror("fopen");
//        return 1;
//    }
//    //读开文件
//    char arr[20] = "xxxxxxxxxxxxxxxxxx";
//    fgets(arr, 5, pf);
//      //关闭文件
//    fclose(pf);
//    pf = NULL;
//    return 0;
//}
//5.6 fprintf
//代码块
//int fprintf(FILE * stream, const char* format, ...);
//类比与printf
//int printf( const char* format, ...);
////类⽐printf函数学习 
//功能： fprintf 是将格式化数据写⼊指定⽂件流的函数。它与 printf 类似，但可以输出到任意
//输出流（如磁盘⽂件、标准输出、标准错误等），⽽不仅限于控制台。
//参数：
//• stream ：指向FILE 对象的指针，表⽰要写⼊的⽂件流（如stdout 、⽂件指针等）。
//• format ：格式化字符串，包含要写⼊的⽂本和格式说明符（如% d 、% s 等）。
//• ... ：可变参数列表，提供与格式字符串中说明符对应的数据。
//返回值：
//• 成功时，返回写⼊的字符总数（⾮负值）。
//• 失败时，先设置对应流的错误指⽰器，再返回负值，可以通过 ferror() 来检测。
//代码演示
//#include <stdio.h>
//struct stu
//{
//    char name[20];
//    int age;
//    float socre;
//};
//struct stu s = { "cuihua",18,85.f };
//int main()
//{
//    //打开文件
//    FILE*pf = fopen("wang.txt", "w");
//    if (pf == NULL)
//    {
//        perror("fopen\n");
//        return 1;
//    }
//    //写文件
//    fprintf(pf,"%s %d %f", s.name, s.age, s.socre);
//    //关闭文件
//    fclose(pf);
//    pf = NULL;
//    return 0;
//}
//5.7 fscanf
//代码块
//int fscanf(FILE * stream, const char* format, ...);
//类比于scanf
//int scanf( const char* format, ...);
////类⽐scanf函数来学习 
//功能： fscanf 是从指定⽂件流中读取格式化数据的函数。它类似于scanf ，但可以指定输⼊源
//（如⽂件、标准输⼊等），⽽⾮仅限于控制台输⼊。适⽤于从⽂件解析结构化数据（如整数、浮点
//数、字符串等）。
//参数：
//• stream ：指向FILE 对象的指针，表⽰要读取的⽂件流（如stdin 、⽂件指针等）。
//• format ：格式化字符串，定义如何解析输⼊数据（如% d 、% f 、% s 等）。
//• ... ：可变参数列表，提供存储数据的变量地址（需与格式字符串中的说明符匹配）。
//返回值：
//• 成功时，函数返回成功填充到参数列表中的项数。该值可能与预期项数⼀致，也可能因以下原因少
//于预期（甚⾄为零）：
//◦ 格式和数据匹配失败；
//◦ 读取发⽣错误；
//◦ 到达⽂件末尾（EOF）。
//• 如果在成功读取任何数据之前发⽣：
//◦ 发⽣读取错误，会在对应流上设置错误指⽰符，则返回EOF 。
//◦ 到达⽂件末尾，会在对应流上设置⽂件结束指⽰符，则返回EOF 。
//假设wang.txt中已经存放了cuihua 18 85.000000
//#include <stdio.h>
//struct stu
//{
//    char name[20];
//    int age;
//    float socre;
//};
//
//int main()
//{
//    struct stu s = { 0 };
//    //打开文件
//    FILE* pf = fopen("wang.txt", "r");
//    if (pf == NULL)
//    {
//        perror("fopen\n");
//        return 1;
//    }
//    //读文件
//    fscanf(pf,"%s %d %f", s.name, &(s.age), &(s.socre));
//    printf("%s %d %f", s.name, (s.age), (s.socre));
//    //输出结果：cuihua 18 85.000000
//    //关闭文件
//    fclose(pf);
//    pf = NULL;
//    return 0;
//}
//5.8 fwrite
//size_t fwrite(const void* ptr, size_t size, size_t count, FILE * stream);
//功能：函数⽤于将数据块写⼊ stream 指向的⽂件流中，是以2进制的形式写⼊的。
//参数：
//• ptr ：指向要写⼊的数据块的指针。
//• size ：要写⼊的每个数据项的⼤⼩（以字节为单位）。
//• count ：要写⼊的数据项的数量。
//• stream ：指向FILE 类型结构体的指针，指定了要写⼊数据的⽂件流。
//返回值：返回实际写⼊的数据项数量。如果发⽣错误，则返回值可能⼩于count 。
//使⽤注意事项：
//• 需要包含<stdio.h> 头⽂件。
//• 在使⽤fwrite() 之前，需要确保⽂件已经以⼆进制可写⽅式打开。
////• fwrite() 通常⽤于⼆进制数据的写⼊，如果写⼊⽂本数据，请谨慎处理换⾏符和编码等问题。
//#include <stdio.h>
//struct stu
//{
//    char name[20];
//    int age;
//    float socre;
//};
//
//int main()
//{
//    struct stu s = { "cuihua",18,98.f };
//    FILE *pf =fopen("wang.txt", "wb+");
//    if (pf == NULL)
//    {
//        perror("fopen");
//        return 1;
//    }
//    fwrite(&s, sizeof(struct stu), 1, pf);
//    fclose(pf);
//    pf = NULL;
//    return 0;
//}
//5.9 fread
//代码块
//size_t fread(void* ptr, size_t size, size_t count, FILE * stream);
//功能：函数⽤于从 stream 指向的⽂件流中读取数据块，并将其存储到 ptr 指向的内存缓冲区中。
//参数：
//• ptr ：指向内存区域的指针，⽤于存储从⽂件中读取的数据。
//• size ：要读取的每个数据块的⼤⼩（以字节为单位）。
//• count ：要读取的数据块的数量。
//• stream ：指向FILE类型结构体的指针，指定了要从中读取数据的⽂件流。
//返回值：返回实际读取的数据块数量
//使⽤注意事项：
//• 需要包含<stdio.h> 头⽂件。
//• 在使⽤fread() 之前，需要确保⽂件已经以⼆进制可读⽅式打开。
//• ptr 指向的内存区域必须⾜够⼤，以便存储指定数量和⼤⼩的数据块。
//• 如果fread() 成功读取了指定数量的数据块，则返回值等于count ；如果读取数量少于
//count ，则可能已经到达⽂件结尾或者发⽣了错误。
//• 在⼆进制⽂件读取时， fread() 是常⽤的函数，但对于⽂本⽂件读取，通常使⽤fgets()
//或fscanf() 。
//假设wang.txt 中已经以二进制形式写入"cuihua", 18, 98.f
#include <stdio.h>
//struct stu
//{
//    char name[20];
//    int age;
//    float socre;
//};
//
//int main()
//{
//    struct stu s = {0};
//    FILE *pf =fopen("wang.txt", "rb+");
//    if (pf == NULL)
//    {
//        perror("fopen");
//        return 1;
//    }
//    //读文件
//    size_t n =fread(&s, sizeof(struct stu), 1, pf);
//    if (n != 1)
//    {
//        //遇到了文件末尾
//        if (feof(pf))
//        {
//            printf("遇到了文件末尾\n");
//        }
//        //读取发生了错误
//        else if(ferror(pf))
//        {
//            printf("读取过程中发生了错误\n");
//        }
//    }
//    printf("%s %d %f", s.name, s.age, s.socre);
//    fclose(pf);
//    pf = NULL;
//    return 0;
//}
//5.10 对⽐⼀组函数：
//scanf / fscanf / sprintf
//printf / fprintf / sprintf
//scanf printf：针对标准输入/标准输出的格式化输入/输出函数
//fprintf fscanf：针对所有输入流/所有输出流的格式化输入 / 输出函数
//sprintf sprintf：将格式化的数据转为字符串/将字符串转为格式化的数据
//5.10.1 sprintf
//代码块
//printf:将格式化的数据打印到屏幕上
//int sprintf(char* str, const char* format, ...);
//功能：将格式化数据写⼊字符数组（字符串）。它类似于printf ，但输出⽬标不是控制台或⽂件，
//⽽是⽤⼾指定的内存缓冲区。常⽤于动态⽣成字符串、拼接数据或转换数据格式。简⽽⾔之就是将格
//式化的数据转换成⼀个字符串。
//参数：
//• str ：指向字符数组的指针，⽤于存储⽣成的字符串（需确保⾜够⼤以防⽌溢出）。
//• format ：格式化字符串，定义输出格式（如% d 、% f 、% s 等）。
//• ... ：可变参数列表，提供与格式字符串中说明符对应的数据。
//返回值：
//• 成功时：返回写⼊buffer 的字符数（不包括结尾的空字符\0 ）。
//• 失败时：返回负值。
//代码演示
//#include <stdio.h>
//struct stu
//{
//    char name[20];
//    int age;
//    float socre;
//};
//int main()
//{
//    struct stu s = { "wangwu",20,99.f };
//    char buf[100] = { 0 };
//    sprintf(buf,"%s %d %f", s.name, s.age, s.socre);
//    printf("%s ",buf);//以字符串的形式打印
//    return 0;
//}
//5.10.2 sscanf
//scanf:从键盘上读取格式化的数据放到变量中
//代码块
//int sscanf(const char* str, const char* format, ...);
//功能：从字符串中读取格式化数据。它与scanf 类似，但输⼊源是内存中的字符串⽽⾮控制台或⽂
//件。常⽤于解析字符串中的结构化数据（如提取数字、分割⽂本等）。
//参数：
//• str ：要解析的源字符串（输⼊数据来源）。
//• format ：格式化字符串，定义如何解析数据（如% d 、% f 、% s 等）。
//• ... ：可变参数列表，提供存储数据的变量地址（需与格式字符串中的说明符匹配）
//返回值：
//• 成功时：返回成功解析并赋值的参数数量（⾮负值）。
//• 失败或未匹配任何数据：若输⼊结束或解析失败，返回EOF （通常是 - 1 ）。
//#include <stdio.h>
//struct stu
//{
//    char name[20];
//    int age;
//    float socre;
//};
//int main()
//{
//    struct stu s = { "wangwu",20,99.f };
//    char buf[100] = { 0 };
//    sprintf(buf,"%s %d %f", s.name, s.age, s.socre);
//    printf("%s \n",buf);//以字符串的形式打印
//    struct stu t = { 0 };
//    sscanf(buf,"%s %d %f", t.name, &(t.age), &(t.socre));
//    printf("%s %d %f\n", t.name, t.age, t.socre);//以结构体的形式打印
//    return 0;
//}
//scanf 针对标准输⼊(stdin)的格式化输⼊函数
//printf 针对标准输出(stdout)的格式化输出函数
//fscanf 针对所有输⼊流（可以是⽂件流，也可以是stdin）的格式化输⼊函数
//fprintf 针对所有输出流（可以是⽂件流，也可以是stdout）的格式化输出函数
//sprintf 将格式化的数据转换成字符串
//sscanf 从字符串中提取格式化的数据
//6. ⽂件的随机读写
//6.1 fseek
//根据⽂件指针的位置和偏移量来定位⽂件指针（⽂件内容的光标）。
//1 int fseek(FILE * stream, long int offset, int origin);
//               SEEK_SET :文件开始的位置
//int origin     SEEK_CUR :文件指示器(指针，光标)的位置
//               SEEK_END ：文件末尾
//int main()
//{
//     FILE* pf = fopen("wang.txt", "r");
//
//     if (pf == NULL)
//     {
//         perror("fopen");
//         return 1;
//     }
//     //读文件
//     int ch =fgetc(pf);
//     printf("%c\n", ch);
//     //fseek(pf, 6, SEEK_SET);
//     //fseek(pf, 5, SEEK_CUR);
//     fseek(pf, -3, SEEK_END);
//     ch = fgetc(pf);
//     printf("%c\n", ch);
//     fclose(pf);
//     pf = NULL;
//    return 0;
//}
//6.2 ftell
//返回⽂件指针相对于起始位置的偏移量
//1 long int ftell(FILE * stream);
//6.3 rewind
//让⽂件指针的位置回到⽂件的起始位置
//1 void rewind(FILE * stream);

//int main()
//{
//     FILE* pf = fopen("wang.txt", "w");
//
//     if (pf == NULL)
//     {
//         perror("fopen");
//         return 1;
//     }
//     //写文件
//     fputs("abcdefghi", pf);
//
//     //fseek(pf, 6, SEEK_SET);
//     //fseek(pf, 5, SEEK_CUR);
//     fseek(pf, -3, SEEK_END);
//     fputs("x", pf);
//     int r = ftell(pf);
//     printf("文件的字节数%d ", r);
//     fseek(pf, 0, SEEK_END);
//     rewind(pf);//等价于fseek(pf, 0, SEEK_SET);
//     fclose(pf);
//     pf = NULL;
//    return 0;
//}
//7. ⽂件缓冲区
//ANSIC标准采⽤“缓冲⽂件系统”处理数据⽂件的，所谓缓冲⽂件系统是指系统⾃动地在内存中为程
//序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲
//区，装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据，则从磁盘⽂件中读取数据输⼊
//到内存缓冲区（充满缓冲区），然后再从缓冲区逐个地将数据送到程序数据区（程序变量等）。缓冲
//区的⼤⼩根据C编译系统决定的。
//7.1 fflush
//代码块
//int fflush(FILE * stream);
//功能：强制刷新参数 stream 指定流的缓冲区，确保数据写⼊底层设备。
//• 对输出流：将缓冲区中未写⼊的数据⽴即写⼊⽂件。
//• 对输⼊流：⾏为由具体实现决定，⾮C语⾔标准⾏为（可能清空输⼊缓冲区）
//• 参数为 NULL 时：刷新所有打开的输出流
//1参数：
//stream ：指向⽂件流的指针（如 stdout 、⽂件指针等）
//返回值：成功返回 0 ，失败返回 EOF
//注意事项：
//1. 仅对输出流或更新流（最后⼀次操作为输出）有明确刷新⾏为
//2. 输⼊流的刷新⾏为不可移植（如清空输⼊缓冲区是⾮标准特性）
//3. 程序正常终⽌（ exit ）或调⽤ fclose 时会⾃动刷新，但程序崩溃时缓冲区数据可能丢失
//这⾥可以得出⼀个结论：
//因为有缓冲区的存在，C语⾔在操作⽂件的时候，需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂
//件。
//如果不做，可能导致读写⽂件的问题。
//#include <stdio.h>
//#include <windows.h>
////VS2022 WIN11环境测试 
//int main()
//{
//    FILE* pf = fopen("data.txt", "w");
//    fputs("abcdef", pf);//先将代码放在输出缓冲区 
//    printf("睡眠10秒-已经写数据了，打开test.txt⽂件，发现⽂件没有内容\n");
//    Sleep(10000);
//    printf("刷新缓冲区\n");
//    fflush(pf);//刷新缓冲区时，才将输出缓冲区的数据写到⽂件（磁盘） 
//    //注：fflush 在⾼版本的VS上不能使⽤了 
//    printf("再睡眠10秒-此时，再次打开test.txt⽂件，⽂件有内容了\n");
//    Sleep(10000);
//    fclose(pf);
//    //注：fclose在关闭⽂件的时候，也会刷新缓冲区 
//    pf = NULL;
//    return 0;
//}

//8. 更新⽂件
//在⽂件的打开模式中有三种⽅式值得注意，分别是:"r+", "w+", "a+"，分别是什么意思？
//
//⾏为                        "r+" "            w+" "        a+"
//解释                     可读 / 可写      可读 / 可写     可读 / 可写
//⽂件不存在时              打开失败   ⾃动创建新⽂件        ⾃动创建新⽂件
//⽂件存在时                保留内容    清空内容              保留内容
//初始⽂件指针位置              ⽂件开头      ⽂件开头              ⽂件末尾
//写⼊是否覆盖原有数据是（可定位覆盖） 是（内容已清空，从头写⼊）否(默认是在⽂件末尾写数据)
//典型⽤途          修改⽂件部分内容 创建新⽂件或完全重写旧⽂件   在⽂件末尾追加数据，⽐如记录
//⽇志
//关键要点：
//1. 在写完⽂件后，要继续读⽂件的时候，在读取之前⼀定要使⽤ fflush() 刷新⽂件缓冲区，或者
//使⽤ fseek() ， rewind() 重新定位⽂件指⽰器的位置。
//2. 在读完⽂件后，需要继续写⽂件之前，在写⽂件之前，使⽤ fseek() ， rewind() 重新定位⽂
//件指⽰器的位置。
int main()
{
     FILE* pf = fopen("wang.txt", "w+");

     if (pf == NULL)
     {
         perror("fopen");
         return 1;
     }
     //写文件
     fputs("abcdefghi", pf);
     //刷新缓冲区
     fflush(pf);
     //读文件
     fseek(pf, 0, SEEK_SET);//定位
     int ch = fgetc(pf);
     printf("%c ", ch);
     pf = NULL;
    return 0;
}